#ifndef MEMORY_UNINITIAL
#define MEMORY_UNINITIAL

#include <utility/spw_utility.h>
#include <type_traits>
#include <algorithm/spw_algobase.h>
#include <iterator/spw_iterator.h>
#include <memory/spw_alloc.h>

/* uninitializedCopy */
template<bool TrivialType>
struct uninitializedCopy_in
{
   template<typename InputIt, typename ForwardIt>
   static ForwardIt s_uninitCopy_in(InputIt first, InputIt last, ForwardIt result)
   {
        using ValueType = typename spwstd::IteratorTraits<ForwardIt>::ValueType;
        ForwardIt cur = result;
        try {
            for(; first!=last; ++first, ++cur) {
                spwstd::AllocInterface<ValueType>::s_construct(spwstd::spwuti::addressOf(*cur), *first);
            }
            return cur;
        } catch(...) {
            ForwardIt tmp = result;
            for(;tmp != cur; ++tmp) {
                spwstd::AllocInterface<ValueType>::s_destroy(spwstd::spwuti::addressOf(*tmp));
                throw;
            }
        }
        return cur;
   }
};

template<>
struct uninitializedCopy_in<true>
{
    template<typename InputIt, typename ForwardIt>
    static ForwardIt s_uninitCopy_in(InputIt first, InputIt last, ForwardIt result)
    { return spwstd::spwalg::copy(first, last, result); }
};

namespace spwstd {
template<typename InputIter, typename ForwardIter>
inline ForwardIter
uninitializedCopy(InputIter first, InputIter last, ForwardIter result)
{
    using ValueType1 = typename spwstd::IteratorTraits<ForwardIter>::ValueType;
    using ValueType2 = typename spwstd::IteratorTraits<InputIter>::ValueType;

    return uninitializedCopy_in<std::is_trivial_v<ValueType1>&&std::is_trivial_v<ValueType2>>::s_uninitCopy_in(first, last, result);
}

/* uninitializedFill */
template<bool TrivialType>
struct uninitializedFill_in
{
   template<typename ForwardIt, typename Tp>
   static void s_uninitFill_in(ForwardIt first, ForwardIt last, const Tp& x)
   {
        using ValueType = typename spwstd::IteratorTraits<ForwardIt>::ValueType;
        ForwardIt cur = first;
        try {
            for(; cur!=last; ++cur) {
                spwstd::AllocInterface<ValueType>::s_construct(spwstd::spwuti::addressOf(*cur), x);
            }
        } catch(...) {
            ForwardIt tmp = first;
            for(;tmp != cur; ++tmp) {
                spwstd::AllocInterface<ValueType>::s_destroy(spwstd::spwuti::addressOf(*tmp));
                throw;
            }
        }
   }
};

template<>
struct uninitializedFill_in<true>
{
    template<typename ForwardIt, typename Tp>
    static void s_uninitFill_in(ForwardIt first, ForwardIt last, const Tp& x)
    { spwstd::spwalg::fill(first, last, x); }
};

template<typename ForwardIter, typename Tp>
inline void
uninitializedFill(ForwardIter first, ForwardIter last, const Tp& x)
{
    using ValueType = typename spwstd::IteratorTraits<ForwardIter>::ValueType;
    const bool isassignable = std::is_copy_assignable_v<ValueType>;
    uninitializedFill_in<std::is_trivial_v<ValueType> && isassignable>::s_uninitFill_in(first, last, x);
}

/* uninitializedFillN */
template<bool TrivialType>
struct uninitializedFillN_in
{
    template<typename ForwardIter, typename Size, typename Tp>
    static ForwardIter s_uninitFillN_in(ForwardIter first, Size n, const Tp& x)
    {
        using ValueType = typename spwstd::IteratorTraits<ForwardIter>::ValueType;
        ForwardIter cur = first;
        try {
            for(; n>0;--n, ++cur) {
                spwstd::AllocInterface<ValueType>::s_construct(spwstd::spwuti::addressOf(*cur), x);
            }
            return cur;
        } catch(...) {
            ForwardIter tmp = first;
            for(;tmp != cur; ++tmp) {
                spwstd::AllocInterface<ValueType>::s_destroy(spwstd::spwuti::addressOf(*tmp));
                throw;
            }
        }
    }
};

template<>
struct uninitializedFillN_in<true>
{
    template<typename ForwardIter, typename Size, typename Tp>
    static ForwardIter s_uninitFillN_in(ForwardIter first, Size n, const Tp& x) 
    {
        return spwstd::spwalg::fillN(first, n, x);
    }
};

template<typename ForwardIter, typename Size, typename Tp>
inline ForwardIter
uninitializedFillN(ForwardIter first, Size n, const Tp& x)
{
    using ValueType = typename spwstd::IteratorTraits<ForwardIter>::ValueType;
    const bool isassignable = std::is_copy_assignable_v<ValueType>;
    return uninitializedFillN_in<std::is_trivial_v<ValueType> && isassignable>::s_uninitFillN_in(first, n, x);
}

} // namespace spwstd
#endif // MEMORY_UNINITIAL
